package uk.ac.aber.paws.client.core;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 16th August 2011
 * 
 * PAWSThreads.java
 * 
 * Contains all generic constants used within the application
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_HEALTH_CHECK;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_IDLE_SESSIONS_CLEANUP;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_MONITOR_USER_LOGINS;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_PARSE_QUEUED_CONNECTIONS;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_PARSE_SAVED_DB_CONNECTIONS;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_START_INPUT_SIMULATOR;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_START_SERVERSOCKET_LISTENER;
import static uk.ac.aber.paws.client.constants.Constants.RUNJOB_START_WAKE_DETECTOR;

import java.util.Calendar;
import java.util.logging.Level;

public class PAWSThreads {

	/**
	 * This thread object opens a SecureSocketServer instance and waits for connections
	 */
	private static RunJob secureServerControlThread = null;
	
	/**
	 * This thread object simulates an input - useful to prevent windows sleeping the computer a minute after waking up
	 */
	private static RunJob inputSimulatorThread = null;
	
	/**
	 * This thread object monitoring the waking status of the computer and detects a wake event
	 */
	private static RunJob registerThread = null;
	
	/**
	 * This thread object holds the thread reference that monitors the health status of the other threads
	 */
	private static RunJob healthChecker = null;
	
	private static RunJob idleSessionsCleaner = null;
	
	/**
	 * This thread object holds the thread reference that checks for updates
	 */
	private static RunJob updateCheckerThread = null;
	
	/**
	 * This object processes any db calls that need to made to the server.
	 */
	private static RunJob processQueuedDBconnectionsThread = null;

	/**
	 * Stores the reference to the process that monitors the logins
	 */
	private static RunJob monitorLogin;
	
	public PAWSThreads() {
		
		startThreads();
	}
	
	public void checkThreadHealth(long startTime) {
		
		if (Calendar.getInstance().getTimeInMillis() > startTime) {
			
			if (monitorLogin != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Monitor Login Health: "+monitorLogin.getState().toString());
				if (!monitorLogin.isAlive()  || monitorLogin.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Login Monitor");
					monitorLogin = actionThread(true,monitorLogin,RUNJOB_MONITOR_USER_LOGINS,"app.main.processinterrupts.loginmonitoringperiod");
				}	
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Starting Login Monitor");
				monitorLogin = actionThread(true,monitorLogin,RUNJOB_MONITOR_USER_LOGINS,"app.main.processinterrupts.loginmonitoringperiod");
			}
			
			if (idleSessionsCleaner != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Idle Session Cleaner Health: "+idleSessionsCleaner.getState().toString());
				if (!idleSessionsCleaner.isAlive()  || idleSessionsCleaner.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Idle Session Cleaner Thread");
					idleSessionsCleaner = actionThread(true,idleSessionsCleaner,RUNJOB_IDLE_SESSIONS_CLEANUP,"app.main.processinterrupts.idleloggerreport");
				}	
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Idle Session Cleaner Thread");
				//registerThread = actionThread(true,registerThread,RUNJOB_START_WAKE_DETECTOR,"app.main.processinterrupts.wakedetector");
			}
			
			
			if (secureServerControlThread != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Secure Server Socket Health: "+secureServerControlThread.getState().toString());
				if (!secureServerControlThread.isAlive()  || secureServerControlThread.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Secure Server Listener");
					secureServerControlThread = actionThread(true,secureServerControlThread,RUNJOB_START_SERVERSOCKET_LISTENER,null);
				}	
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Starting Secure Server Listener");
				secureServerControlThread = actionThread(true,secureServerControlThread,RUNJOB_START_SERVERSOCKET_LISTENER,null);
			}
			
			if (processQueuedDBconnectionsThread != null) {
				Initiator.LOG_FILE.log(Level.ALL,"DB Processing Health: "+processQueuedDBconnectionsThread.getState().toString());
				if (!processQueuedDBconnectionsThread.isAlive()  || processQueuedDBconnectionsThread.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Database Logger");
					processQueuedDBconnectionsThread = actionThread(true,processQueuedDBconnectionsThread,RUNJOB_PARSE_QUEUED_CONNECTIONS,"app.main.processinterrupts.processdbcalls");
				}
				
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Starting Database Logger");
				processQueuedDBconnectionsThread = actionThread(true,processQueuedDBconnectionsThread,RUNJOB_PARSE_QUEUED_CONNECTIONS,"app.main.processinterrupts.processdbcalls");
			}
			
			
			
			/*if (updateCheckerThread != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Update Checker Health: "+updateCheckerThread.getState().toString());		
				if (!updateCheckerThread.isAlive() || updateCheckerThread.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Updating Checker");
					updateCheckerThread = actionThread(true,updateCheckerThread,RUNJOB_CHECK_FOR_UPDATE,"app.main.processinterrupts.checkforupdates");
				}
				
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Restarting Updating Checker");
				updateCheckerThread = actionThread(true,updateCheckerThread,RUNJOB_CHECK_FOR_UPDATE,"app.main.processinterrupts.checkforupdates");
			}*/
			
			
			if (inputSimulatorThread != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Input Simulator Health: "+inputSimulatorThread.getState().toString());
				if (!inputSimulatorThread.isAlive()  || inputSimulatorThread.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Input Simulator");
					inputSimulatorThread = actionThread(true,inputSimulatorThread,RUNJOB_START_INPUT_SIMULATOR,"app.main.processinterrupts.inputsimulator");
				}	
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Starting Input Simulator");
				inputSimulatorThread = actionThread(true,inputSimulatorThread,RUNJOB_START_INPUT_SIMULATOR,"app.main.processinterrupts.inputsimulator");
			}
			
			if (registerThread != null) {
				Initiator.LOG_FILE.log(Level.ALL,"Register Thread Health: "+registerThread.getState().toString());
				if (!registerThread.isAlive()  || registerThread.getState() == Thread.State.BLOCKED) {
					Initiator.LOG_FILE.log(Level.ALL,"Restarting Register Thread");
					registerThread = actionThread(true,registerThread,RUNJOB_START_WAKE_DETECTOR,"app.main.processinterrupts.wakedetector");
				}	
			}
			else {
				Initiator.LOG_FILE.log(Level.ALL,"Starting Register Thread");
				registerThread = actionThread(true,registerThread,RUNJOB_START_WAKE_DETECTOR,"app.main.processinterrupts.wakedetector");
			}
			
			
		}
		else {
			Initiator.LOG_FILE.log(Level.ALL,"Waiting "+(startTime-Calendar.getInstance().getTimeInMillis())+" millisecs before starting this service.");
		}
	}

	public void stopThreads() {
		
		// kill all the active threads
		// stop the health monitor - thread is no longer needed
		actionThread(false,healthChecker,0,null);
		if (monitorLogin != null) actionThread(false,monitorLogin,0,null);
		if (secureServerControlThread != null) actionThread(false,secureServerControlThread,0,null);
		if (inputSimulatorThread != null) actionThread(false,inputSimulatorThread,0,null);
		if (updateCheckerThread != null) actionThread(false,updateCheckerThread,0,null);
		if (registerThread != null) actionThread(false,registerThread,0,null);
		if (processQueuedDBconnectionsThread != null) actionThread(false,processQueuedDBconnectionsThread,0,null);
		if (idleSessionsCleaner != null) actionThread(false,idleSessionsCleaner,0,null);
	}
	
	
	private void startThreads() {
		
		// opens up a socket that listens to idle times reported by idle logger
		secureServerControlThread = actionThread(true,secureServerControlThread,RUNJOB_START_SERVERSOCKET_LISTENER,null);
		
		// parses any cached unregistered database connections that exist in cache/ folder
		actionThread(true,null,RUNJOB_PARSE_SAVED_DB_CONNECTIONS,null);
		
		// processes the queued database calls
		processQueuedDBconnectionsThread = actionThread(true,processQueuedDBconnectionsThread,RUNJOB_PARSE_QUEUED_CONNECTIONS,"app.main.processinterrupts.processdbcalls");
		processQueuedDBconnectionsThread.setPriority(Thread.MAX_PRIORITY);
		
		// check for any updates - every 24 hours
		//updateCheckerThread = actionThread(true,updateCheckerThread,RUNJOB_CHECK_FOR_UPDATE,"app.main.processinterrupts.checkforupdates");
		
		// run the job to monitor login status every 5 seconds
		monitorLogin = actionThread(true,monitorLogin,RUNJOB_MONITOR_USER_LOGINS,"app.main.processinterrupts.loginmonitoringperiod");
		
		// Input Simulator Thread
		inputSimulatorThread = actionThread(true,inputSimulatorThread,RUNJOB_START_INPUT_SIMULATOR,"app.main.processinterrupts.inputsimulator"); 
		
		// clean up idle sessions
		idleSessionsCleaner = actionThread(true,idleSessionsCleaner,RUNJOB_IDLE_SESSIONS_CLEANUP,"app.main.processinterrupts.idleloggerreport");
		
		// job that every 5 seconds triggers a register, should a predefined period be exceeded, then
		// it assumes that the computer has just woke from sleep and acts accordingly
		registerThread = actionThread(true,registerThread,RUNJOB_START_WAKE_DETECTOR,"app.main.processinterrupts.wakedetector");
		registerThread.setPriority(Thread.MAX_PRIORITY);
		
		// sleep for 5 seconds and then unblock any blocked threads
		try {
			Thread.currentThread().sleep(5000);
		} 
		catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	
	public void startHealthCheckThread() {
		
		// health check monitor that monitor the health of the other threads
		healthChecker = actionThread(true,healthChecker,RUNJOB_HEALTH_CHECK,"app.main.processinterrupts.healthcheck");
	}
	
	
	/**
	 * This procedure kicks off or kills the required thread
	 * @param Specify true to create the thread
	 * @param The reference to the thread - mainly used in the kill functionality
	 * @param The key identifier to be used by RunJob in order to identify the job to be created
	 * @param The key string to the configuration for identifying the thread sleep period between job execution
	 * @return The reference to the newly created RunJob object for when new Threads are created
	 */
	private static RunJob actionThread(boolean state, RunJob thread, int key, String sleepKey) {
		
		if (state) {
			
			if (sleepKey == null) {
				thread = new RunJob(key,-1);
			}
			else {
				thread = new RunJob(key,Integer.parseInt(PAWSClient.getConfig().getProperty(sleepKey)));
			}
		}
		else {
			thread.interrupt();
		}
		
		return thread;
	}
	
}
